import plotly.io as pio
pio.renderers.default = 'notebook_connected'
서울시 내 출범한 스타벅스의 위치 및 인구통계 데이터를 통해,
위의 가설들을 확인해보려 함
# 필요한 라이브러리 임포팅
from selenium import webdriver
from bs4 import BeautifulSoup
import pandas as pd
import time
# 스타벅스 매장 정보 접속
browser = webdriver.Chrome("C:/Users/DooDoo/Documents/data_analysis/chromedriver.exe")
url = "https://www.starbucks.co.kr/store/store_map.do?disp=locale"
browser.get(url)
time.sleep(3)
# 서울에 있는 전체 매장 클릭
seoul_btn = "#container > div > form > fieldset > div > section > article.find_store_cont > article > article:nth-child(4) > div.loca_step1 > div.loca_step1_cont > ul > li:nth-child(1) > a"
browser.find_element_by_css_selector(seoul_btn).click()
all_btn = "#mCSB_2_container > ul > li:nth-child(1) > a"
browser.find_element_by_css_selector(all_btn).click()
time.sleep(3)
# beautifulsoup 이용, 매장데이터만 파싱
html = browser.page_source
soup = BeautifulSoup(html, 'html.parser')
# 매장 정보만 추출
starbucks_soup_list = soup.select("div.mCSB_container > ul > li.quickResultLstCon")
# 533개의 매장이 존재
len(starbucks_soup_list)
starbucks_soup_list[0]
#매장별 데이터프레임 생성
starbucks_list = []
for item in starbucks_soup_list:
name = item['data-name']
lat = item['data-lat']
long = item['data-long']
store_type = item.select('i')[0]['class'][0][4:]
address = str(item.select('p.result_details')[0]).split('<br/>')[0].split('>')[1]
tel = str(item.select('p.result_details')[0]).split('<br/>')[1].split('<')[0]
starbucks_list.append([name, lat, long, store_type, address, tel])
# 생성된 데이터 프레임, 전화번호는 다 대표전화로 바꿔놓은듯...
columns = ['매장명', '위도', '경도', '매장타입', '주소', '전화번호']
seoul_starbucks_df = pd.DataFrame(data = starbucks_list, columns = columns)
seoul_starbucks_df.head()
#생성된 데이터 확인
seoul_starbucks_df.info()
import requests
import pandas as pd
# api 호출을 위한 함수
def seoul_open_api_data(url, service):
data_list = None
try:
result_dict = requests.get(url).json()
result_data = result_dict[service]
code = result_data['RESULT']['CODE']
if code == 'INFO-000':
data_list = result_data['row']
except:
pass
return data_list
#시군구 데이터 호출
# 서울열린데이터광장 API key
seoul_api_auth_key = "664f517959647569383372716e6e51"
# get url
url = "http://openapi.seoul.go.kr:8088/{}/json/SdeTlSccoSigW/1/25/".format(seoul_api_auth_key)
sgg_data_list = seoul_open_api_data(url, "SdeTlSccoSigW")
sgg_data_list[0]
# 데이터 프레임으로 변환
columns = ['SIG_CD', 'SIG_KOR_NM', 'LAT', 'LNG']
sgg_df = pd.DataFrame(data = sgg_data_list, columns = columns)
sgg_df.head()
# 컬럼명 변경
sgg_df.columns = ['시군구코드', '시군구명', '위도', '경도']
sgg_df.head()
# 함수를 이용하여 시군구별 인구 데이터 가져오기
pop_url = 'http://openapi.seoul.go.kr:8088/{}/json/octastatapi419/1/26/'.format(seoul_api_auth_key)
pop_data_list = seoul_open_api_data(pop_url, 'octastatapi419')
# API 제공 중단으로 직접 데이터 다운로드
sgg_pop_df = pd.read_csv("report.txt", delimiter= '\t', header = 2, thousands= ',')
sgg_pop_df = sgg_pop_df[1:]
sgg_pop_df_final = sgg_pop_df[['자치구', '계']]
sgg_pop_df_final.columns = ['시군구명', '주민등록인구']
sgg_pop_df_final.head()
# 사업체 데이터 또한 제공 중단으로 직접 다운로드
sgg_biz_df = pd.read_csv("company_report.txt", delimiter= '\t', header = 2, thousands= ',')
sgg_biz_df = sgg_biz_df[sgg_biz_df['동'] == '소계']
sgg_biz_df_final = sgg_biz_df[['자치구', '계', '사업체수']].reset_index(drop = True)
sgg_biz_df_final.columns = ['시군구명', '종사자수', '사업체수']
sgg_biz_df_final.head()
# 앞에서 만든 데이터들을 통합
## 스타벅스 주소에서 시군구명 추출
sgg_names = []
for address in seoul_starbucks_df['주소']:
sgg = address.split()[1]
sgg_names.append(sgg)
seoul_starbucks_df['시군구명'] = sgg_names
seoul_starbucks_df.head()
# 시군구 별로 스타벅스 매장 세기
seoul_starbucks_count = seoul_starbucks_df.pivot_table(
index = '시군구명',
values = '매장명',
aggfunc = 'count').rename(columns = {'매장명':'스타벅스_매장수'})
seoul_starbucks_count.head()
# 시군구 목록 데이터에 스타벅스 매장 수 병합
seoul_sgg = pd.merge(sgg_df, seoul_starbucks_count, how = "left", on = '시군구명')
seoul_sgg.head()
# 시군구별 인구 수 병합
seoul_sgg = pd.merge(seoul_sgg, sgg_pop_df_final, how = "left", on = '시군구명')
seoul_sgg.head()
# 시군구별 사업체 종사자 및 사업체 수 병합
seoul_sgg = pd.merge(seoul_sgg, sgg_biz_df_final, how = "left", on = '시군구명')
seoul_sgg.head()
import folium
import json
# 서울 중심으로 folium 지도 생성
starbucks_map = folium.Map(
location = [37.55875, 126.98745],
tiles = 'Stamen Terrain',
zoom_start = 11
)
starbucks_map
#서울 스타벅스 위치 서클 마커 그리기
for idx in seoul_starbucks_df.index:
lat = seoul_starbucks_df.loc[idx, '위도']
lng = seoul_starbucks_df.loc[idx, '경도']
folium.CircleMarker(
location=[lat, lng],
fill = True,
fill_color = 'green',
fill_opacity = 1,
color = 'yellow',
weight = 1,
radius = 3
).add_to(starbucks_map)
starbucks_map
#매장별로 시각화 시도
starbucks_map2 = folium.Map(
location = [37.55875, 126.98745],
tiles = 'Stamen Terrain',
zoom_start = 11
)
for idx in seoul_starbucks_df.index:
lat = seoul_starbucks_df.loc[idx, '위도']
lng = seoul_starbucks_df.loc[idx, '경도']
store_type = seoul_starbucks_df.loc[idx, '매장타입']
fillColor = ''
if store_type == 'general':
fillColor = 'gray'
size = 1
elif store_type == 'reserve':
fillColor = 'blue'
size = 5
elif store_type == 'generalDT':
fillColor = 'red'
size = 5
folium.CircleMarker(
location=[lat, lng],
fill = True,
fill_color = fillColor,
fill_opacity = 1,
color = 'yellow',
weight = 1,
radius = size
).add_to(starbucks_map2)
starbucks_map2
# 서울시 시군구 행정 경계 지도 불러오기
sgg_geojson_file_path = "./maps/seoul_sgg.geojson"
seoul_sgg_geo = json.load(open(sgg_geojson_file_path, encoding='utf-8'))
seoul_sgg_geo['features'][0]['properties']
starbucks_bubble = folium.Map(
location = [37.55875, 126.98745],
tiles = 'CartoDB dark_matter',
zoom_start = 11
)
def style_function(feature):
return {
'opacity' : 0.7,
'weight' : 1,
'color' : 'white',
'fillOpacity' : 0,
'dashArray': '5, 5',
}
folium.GeoJson(
seoul_sgg_geo,
style_function = style_function
).add_to(starbucks_bubble)
starbucks_bubble
# 시군구별 매장 수와, 평균 매장 수와 비교에서 더 많은지 큰지도 색깔로 표시
starbucks_mean = seoul_sgg['스타벅스_매장수'].mean()
print(starbucks_mean)
for idx in seoul_sgg.index:
lat = seoul_sgg.loc[idx, '위도']
lng = seoul_sgg.loc[idx, '경도']
count = seoul_sgg.loc[idx, '스타벅스_매장수']
if count > starbucks_mean:
fillColor = '#FF0000'
else:
fillColor = '#CCFF33'
folium.CircleMarker(
location=[lat, lng],
fill_color = fillColor,
fill_opacity = 0.7,
color = '#FFFF00',
weight = 1.5,
radius = count/2
).add_to(starbucks_bubble)
starbucks_bubble
# 같은 데이터를 단계구분도로 시각화
starbucks_choropleth = folium.Map(
location = [37.55875, 126.98745],
tiles = 'CartoDB dark_matter',
zoom_start = 11
)
folium.Choropleth(
geo_data = seoul_sgg_geo,
data = seoul_sgg,
columns = ['시군구명', '스타벅스_매장수'],
fill_color= 'YlGn',
fill_opacity= 0.7,
line_opacity= 0.5,
key_on = 'properties.SIG_KOR_NM'
).add_to(starbucks_choropleth)
starbucks_choropleth
# 시군구별 인구 수의 단계분포도 위에 스타벅스 매장 수의 버블지도를 그려봅니다
starbucks_choropleth = folium.Map(
location = [37.55875, 126.98745],
tiles = 'CartoDB dark_matter',
zoom_start = 11
)
folium.Choropleth(
geo_data = seoul_sgg_geo,
data = seoul_sgg,
columns = ['시군구명', '주민등록인구'],
fill_color = 'YlGn',
fill_opacity = 0.7,
line_opacity = 0.5,
key_on = 'properties.SIG_KOR_NM'
).add_to(starbucks_choropleth)
for idx in seoul_sgg.index:
lat = seoul_sgg.loc[idx, '위도']
lng = seoul_sgg.loc[idx, '경도']
count = seoul_sgg.loc[idx, '스타벅스_매장수']
if count > starbucks_mean:
fillColor = '#FF0000'
else:
fillColor = '#CCFF33'
folium.CircleMarker(
location=[lat, lng],
fill_color = fillColor,
fill_opacity = 0.7,
color = '#FFFF00',
weight = 1.5,
radius = count/2
).add_to(starbucks_choropleth)
starbucks_choropleth
# 인구 1만명당 스타벅스 매장수
seoul_sgg['만명당_매장수'] = seoul_sgg['스타벅스_매장수'] / (seoul_sgg['주민등록인구'] / 10000)
# 1만명당 매장 수가 상위 10%인 시군구만 빨간색으로
viz_map_1 = folium.Map(
location = [37.55875, 126.98745],
tiles = 'CartoDB dark_matter',
zoom_start = 11
)
def style_function(feature):
return {
'opacity' : 0.7,
'weight' : 1,
'color' : 'white',
'fillOpacity' : 0,
'dashArray': '5, 5',
}
folium.GeoJson(
seoul_sgg_geo,
style_function = style_function
).add_to(viz_map_1)
top = seoul_sgg['만명당_매장수'].quantile(0.9)
for idx in seoul_sgg.index:
lat = seoul_sgg.loc[idx, '위도']
lng = seoul_sgg.loc[idx, '경도']
r = seoul_sgg.loc[idx, '만명당_매장수']
if r > top:
fillColor = '#FF0000'
else:
fillColor = '#CCFF33'
folium.CircleMarker(
location=[lat, lng],
fill_color = fillColor,
fill_opacity = 0.7,
color = '#FFFF00',
weight = 1.5,
radius = r * 10
).add_to(viz_map_1)
viz_map_1
# 시군구별 사업체 종사자 수의 단계분포도 위에 스타벅스 매장 수의 버블지도를 그려봅니다
starbucks_choropleth = folium.Map(
location = [37.55875, 126.98745],
tiles = 'CartoDB dark_matter',
zoom_start = 11
)
folium.Choropleth(
geo_data = seoul_sgg_geo,
data = seoul_sgg,
columns = ['시군구명', '종사자수'],
fill_color = 'YlGn',
fill_opacity = 0.7,
line_opacity = 0.5,
key_on = 'properties.SIG_KOR_NM'
).add_to(starbucks_choropleth)
for idx in seoul_sgg.index:
lat = seoul_sgg.loc[idx, '위도']
lng = seoul_sgg.loc[idx, '경도']
count = seoul_sgg.loc[idx, '스타벅스_매장수']
if count > starbucks_mean:
fillColor = '#FF0000'
else:
fillColor = '#CCFF33'
folium.CircleMarker(
location=[lat, lng],
fill_color = fillColor,
fill_opacity = 0.7,
color = '#FFFF00',
weight = 1.5,
radius = count/2
).add_to(starbucks_choropleth)
starbucks_choropleth
# 인구 1만명당 스타벅스 매장수
seoul_sgg['종사자_만명당_매장수'] = seoul_sgg['스타벅스_매장수'] / (seoul_sgg['종사자수'] / 10000)
viz_map_2 = folium.Map(
location = [37.55875, 126.98745],
tiles = 'CartoDB dark_matter',
zoom_start = 11
)
def style_function(feature):
return {
'opacity' : 0.7,
'weight' : 1,
'color' : 'white',
'fillOpacity' : 0,
'dashArray': '5, 5',
}
folium.GeoJson(
seoul_sgg_geo,
style_function = style_function
).add_to(viz_map_2)
top = seoul_sgg['종사자_만명당_매장수'].quantile(0.9)
for idx in seoul_sgg.index:
lat = seoul_sgg.loc[idx, '위도']
lng = seoul_sgg.loc[idx, '경도']
r = seoul_sgg.loc[idx, '종사자_만명당_매장수']
if r > top:
fillColor = '#FF0000'
else:
fillColor = '#CCFF33'
folium.CircleMarker(
location=[lat, lng],
fill_color = fillColor,
fill_opacity = 0.7,
color = '#FFFF00',
weight = 1.5,
radius = r * 10
).add_to(viz_map_2)
viz_map_2